; First battery of tests: Check how long it takes for the VDP to ack the interrupt,
; and to drive the interrupt line high again.
;
; Test at different cycles relative to the interrupt.

TestAckTiming	proc

		; We have to fight with Pasmo's operator precedence, where
		; unary minus is lower precedence that addition/subtraction,
		; so -a-b is taken as -(a-b), and in some contexts (-a)-b can't
		; be used, e.g. ld hl,(-a)-b gives error. We had two options:
		; (a) work around it, (b) switch to --brackets mode and use
		; parentheses. We chose (a): using +(-a)-b works and 0-a-b works.

		; Synchronize with the interrupt, start counting on return
		call	SyncVInt
		; SyncVInt returns with di, 9T after interrupt, IntVec trashed, ack pending

; *** First test ***
; Check how far away from the interrupt we can acknowledge

					; 9T  ; from SyncVInt
		in	a,(99h)		; 12T ; Acknowledge the interrupt that SyncVInt left pending
		ld	hl,_IntSCF	; 11T
		ld	(IntVec),hl	; 17T
		ld	ix,AckAfterInt	; 16T
		ld	(ix+0),22	; 21T ; Start 22 cycles after interrupt (13 for INT in IM1
					;       + 9 for minimal IN, is the fastest it can be
					;       acknowledged in an ISR)

		ld	hl,0-124+22	; 11T ; Cycles used at input time, and starting value of [AckBeforeInt]

_AckTestLoop:	call	WaitFrmPlusHL	; 18T
		in	a,(99h)		; 9T  ; pre input
					; > input signalled here (IORQ and RD go low)
		; first time: 9+12+11+17+16+21+11+18+9 = 124
		; subsequent times: 3+20+20+5+5+5+ 0+5+13+(-5)+11+25+13 + 18+9 = 147  (no int)
					; 3T  ; post input
		ex	(sp),hl		; 20T
		ex	(sp),hl		; 20T
		xor	a		; 5T
		ei			; 5T
		nop			; 5T  ; Interrupt should be detected immediately after this
					; 0T/46T ; 19(IM2)+11(JP)+5(SCF)+11(RET)=46T from interrupt, if it happens
		di			; 5T
		jr	c,_AckFailed	; 13T ; If an interrupt happens, it means the ack fails
					; -5T ; for taking the false branch
		; The ack succeeded and stopped the interrupt.
		ld	hl,0-147-1	; 11T ; Total cycles during repeat + move the ack 1T earlier
		dec	(ix+0)		; 25T
		jr	_AckTestLoop	; 13T


; *** Second test ***
; Check where, in relation to the interrupt, bit 7 of the status register turns on

					; 122T ; So far: 3+20+20+5+5+5+46+5+13 - (ix+0)
_AckFailed:
		in	a,(99h)		; 12T ; Ack the interrupt for which the previous ack failed
		ld	a,(ix+0)	; 21T  ; This is the number of cycles with respect to the reference position
		inc	(ix+0)		; 25T ; Correct the value so it shows the first ACK, not the last non-ACK
		neg			; 10T
		ld	e,a		; 5T
		rlca			; 5T
		sbc	a,a		; 5T
		ld	d,a		; 5T
		ld	(ix+1),-20	; 21T ; Start 20 frames before the interrupt
		ld	hl,0-310-20	; 11T ; 122+12+21+25+10+5+5+5+5+21+11+12+21+8+18+9 = 310
		add	hl,de		; 12T
		ld	(ix+2),0	; 21T ; Flag to detect falling edges, as they shouldn't happen
					;       Used as temporary (can be overwritten in the next routine)
		ld	b,24		; 8T  ; How many cycles into it to run

; Alternative version that wastes up to 5 frames during syncing, used to validate the above:
;		inc	(ix+0)		; Correct the value so it shows the first ACK, not the last non-ACK
;		ld	(ix+1),-16	; 21T
;		ld	(ix+2),0	; 21T
;		in	a,(99h)		; Clear pending int
;		ld	b,40		; 8T
;		call	SyncVInt
;					; 9T  ; after interrupt
;		in	a,(99h)		; 12T ; Clear int
;		ld	hl,0-59-16	; 11T ; 9+12+11+18+9 = 59

_LoopFindOnes:	call	WaitFrmPlusHL	; 18T
		in	a,(99h)		; 9T  ; pre input
					; 3T  ; post input
		rlca			; 5T
		sbc	a,a		; 5T
		ld	e,a		; 5T
		cpl			; 5T
		and	(ix+2)		; 21T ; nonzero if it was set and now reset
		ld	(ix+2),e	; 21T ; Store current value
		ld	a,3		; 8T  ; Error code 3: "Falling edge while testing bit 7 of status reg"
		jp	nz,Finish	; 11T
		ld	a,(ix+1)	; 21T
		add	a,e		; 5T  ; Increment if e=0, part 1/2 (note e is either 0 or -1)
		inc	a		; 5T  ; Increment if e=0, part 2/2
		ld	(ix+1),a	; 21T
		in	a,(99h)		; 12T ; Ack any pending interrupt
		ld	hl,0-200+1	; 11T ; 3+5+5+5+5+21+21+8+11+21+5+5+21+12+11+14 + 18+9 = 200, +1 to shift phase forwards
		djnz	_LoopFindOnes	; 14T
					; -5T ; for false condition

; *** Third test
; Check how many cycles pass until the ack is effective and the INT is no longer retriggered.
; We can't test this as effectively as we wish; the time seems very short but at least 3 cycles
; (at least in the machines we've tried), and we don't have enough control for the cycle count.
;
; Approach:
; 1) test whether it's up to  2 cycles by using EI / IN A,(99h). No int means the ack takes 0 to 2 cycles.
; 2) test whether it's up to  5 cycles by using EI / INI.
; 3) test whether it's up to 10 cycles by using EI / INIR with a repeat > 1.
; 4) test whether it's up to 12 cycles by using IN A,(99h) / EI / NOP.
; 5) test whether it's up to 13 cycles by using SCF / IN A,(99h) / EI / RET NC.
; 6) test whether it's up to 14 cycles by using IN A,(99h) / EI / INC HL.
; 7) test whether it's up to 15 cycles by using IN A,(99h) / EI / OR 0.
; Else it's 16 or longer (dubious).


; 1) test 0 to 2 cycles
		; Last int was acked, so we need another one
		ld	hl,_IntSCF + 1	; point to a RET
		ld	(IntVec),hl
		ei
		halt
		ld	(ix+2),0
		ld	(ix+3),2
		ld	hl,_IntINCE
		ld	(IntVec),hl

		ld	e,0		; Flag to detect interrupt
		ei
		in	a,(99h)
		di
		dec	e
		ret	nz		; If int not triggered, it's 0 to 2 cycles

; 2) Test 3 to 5 cycles
		ld	hl,_IntSCF + 1	; point to a RET
		ld	(IntVec),hl
		ei
		halt
		ld	hl,_IntINCE
		ld	(IntVec),hl

		ld	(ix+2),3
		ld	(ix+3),5
		ld	d,8
		ld	hl,ScratchAck	; use as scratch area
		ld	bc,99h
		ei
		ini
		di
		dec	e
		ret	nz		; Int not triggered, it's 3 to 5 cycles.

; 3) Test 6 to 10 cycles
		ld	hl,_IntSCF + 1	; point to a RET
		ld	(IntVec),hl
		ei
		halt
		ld	hl,_IntINCE
		ld	(IntVec),hl

		ld	(ix+2),6
		ld	(ix+3),10
		ld	hl,ScratchAck
		ld	bc,299h
		ei
		inir			; If int happens, must be in 1st one, so min 11T, else 6-10.
		di
		dec	e
		ret	nz

; 4) Test 11 to 12 cycles
		ld	hl,_IntSCF + 1	; point to a RET
		ld	(IntVec),hl
		ei
		halt
		ld	hl,_IntINCE
		ld	(IntVec),hl

		ld	(ix+2),11
		ld	(ix+3),12
		in	a,(99h)
		ei
		nop
		di
		dec	e
		ret	nz

; 5) Test 13 cycles
		ld	hl,_IntSCF + 1	; point to a RET
		ld	(IntVec),hl
		ei
		halt
		ld	hl,_IntINCE
		ld	(IntVec),hl

		ld	(ix+2),13
		ld	(ix+3),13
		scf
		in	a,(99h)
		ei
		ret	nc		; always false
		di
		dec	e
		ret	nz

; 6) Test 14 cycles
		ld	hl,_IntSCF + 1	; point to a RET
		ld	(IntVec),hl
		ei
		halt
		ld	hl,_IntINCE
		ld	(IntVec),hl

		ld	(ix+2),14
		ld	(ix+3),14
		scf
		in	a,(99h)
		ei
		inc	hl
		di
		dec	e
		ret	nz

; 7) Test 15 cycles
		ld	hl,_IntSCF + 1	; point to a RET
		ld	(IntVec),hl
		ei
		halt
		ld	hl,_IntINCE
		ld	(IntVec),hl

		ld	(ix+2),15
		ld	(ix+3),15
		scf
		in	a,(99h)
		ei
		or	0
		di
		dec	e
		ret	nz

		; Must be 16 or more
		ld	(ix+2),16
		ld	(ix+3),-1

		ret


_IntSCF:	scf			; 5T
		ret			; 11T

_IntINCE:	inc	e		; 5T
		ret

		endp
